package com.maslyk.invitation.audit;

import java.io.Serializable;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.hibernate.CallbackException;
import org.hibernate.EntityMode;
import org.hibernate.Interceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
import org.springframework.beans.factory.annotation.Autowired;


public class AuditLogInterceptor implements Interceptor {
	
	@Autowired
	private AuditLogUtil auditLogUtil;

	Logger log = Logger.getLogger(AuditLogInterceptor.class);

	public boolean onSave(Object entity, Serializable id, Object[] state,
			String[] propertyNames, Type[] types) throws CallbackException {
		
		StringBuilder details = new StringBuilder();
		Class<? extends Object> entityClazz = entity.getClass();
		for(int i=0; i< state.length; i++){
			
			PropertyLabel annotationLabel = null;
			try {
				annotationLabel = entityClazz.getDeclaredField(propertyNames[i]).getAnnotation(PropertyLabel.class);
			} catch (NoSuchFieldException e) {
				log.info("Błąd pobrania annotacji: "+propertyNames[i]);
			} catch (Exception e) {
			log.error("Inny błąd pobrania annotacji: "+propertyNames[i],e);
		}
			
			// pole nie opisane przeze mnie wiec opuszczam
			if(annotationLabel == null)
				continue;
			details.append(annotationLabel.label()).append(": ").append(String.valueOf(state[i])).append('\n');
		}

		auditLogUtil.LogIt(OperationType.INSERT, entity, details.toString());
		return false;
	}

	public boolean onFlushDirty(Object entity, Serializable id,
			Object[] currentState, Object[] previousState,
			String[] propertyNames, Type[] types) throws CallbackException {
		
		StringBuilder details = new StringBuilder();
		Class<? extends Object> entityClazz = entity.getClass();
		
		
		for(int i=0; i< currentState.length; i++)
		{
			PropertyLabel annotationLabel = null;
			try {
				annotationLabel = entityClazz.getDeclaredField(propertyNames[i]).getAnnotation(PropertyLabel.class);
			} catch (Exception e) {
				log.error("Błąd pobrania annotacji \n", e);
			}
			
			// pole nie opisane przeze mnie wiec opuszczam
			if(annotationLabel == null)
				continue;
			
			// jesli pola sa takie same to nie logujemy zmian
			if(equality(previousState[i],currentState[i]))
				continue;
			
			details.append(annotationLabel.label()).append(": ");
			details.append(String.valueOf(previousState[i]));
			details.append(" -> ").append(String.valueOf(currentState[i])).append('\n');	
		}
		
		auditLogUtil.LogIt(OperationType.UPDATE, entity, details.toString());
		return false;

	}

	public void onDelete(Object entity, Serializable id, Object[] state,
			String[] propertyNames, Type[] types) {
		
		StringBuilder details = new StringBuilder();
		Class<? extends Object> entityClazz = entity.getClass();
		for(int i=0; i< state.length; i++){
			
			PropertyLabel annotationLabel = null;
			try {
				annotationLabel = entityClazz.getDeclaredField(propertyNames[i]).getAnnotation(PropertyLabel.class);
			} catch (Exception e) {
				log.error("Błąd pobrania annotacji \n", e);
			}
			
			// pole nie opisane przeze mnie wiec opuszczam
			if(annotationLabel == null)
				continue;
			
			details.append(annotationLabel.label()).append(": ").append(String.valueOf(state[i])).append('\n');
		}

		auditLogUtil.LogIt(OperationType.DELETE, entity, details.toString());
	}

	// called before commit into database
	public void preFlush(Iterator iterator) {
	}

	// called after committed into database
	public void postFlush(Iterator iterator) {

	}

	public void afterTransactionBegin(Transaction arg0) {
	}

	public void afterTransactionCompletion(Transaction arg0) {
	}

	public void beforeTransactionCompletion(Transaction arg0) {

	}

	public int[] findDirty(Object arg0, Serializable arg1, Object[] arg2,
			Object[] arg3, String[] arg4, Type[] arg5) {
		return null;
	}

	public Object getEntity(String arg0, Serializable arg1)
			throws CallbackException {
		return null;
	}

	public String getEntityName(Object arg0) throws CallbackException {
		return null;
	}

	public Object instantiate(String arg0, EntityMode arg1, Serializable arg2)
			throws CallbackException {
		return null;
	}

	public Boolean isTransient(Object entity) {
		return null;
	}

	public void onCollectionRecreate(Object arg0, Serializable arg1)
			throws CallbackException {
	}

	public void onCollectionRemove(Object arg0, Serializable arg1)
			throws CallbackException {
	}

	public void onCollectionUpdate(Object arg0, Serializable arg1)
			throws CallbackException {
	}

	public boolean onLoad(Object arg0, Serializable arg1, Object[] arg2,
			String[] arg3, Type[] arg4) throws CallbackException {
		return false;
	}

	public String onPrepareStatement(String sql) {
		return sql;
	}
	
	private boolean equality(Object obj1, Object obj2)
	{
		if(obj1 != null && obj2 != null)
			return obj1.equals(obj2);
		
		if(obj1 == null && obj2==null)
			return true;
		
		if(obj1 != null && obj2 == null)
			return false;
		
		if(obj1 == null && obj2 != null)
			return false;
		
		return false;
	}
}
